/* Copyright 2008. All rights reserved. */
package org.bling.parser.style;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.bling.swt.BlingLayoutData;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;

/**
 * <code>StyleParser</code> is the component which applys visible styling to
 * a SWT control.  For example, the StyleParser will handle commands to
 * change the height/width, or foreground/background color.
 * 
 *  Also, information about the location or 'float' of the control will be stored
 *  for the BlingLayout & BlingLayoutHelper to utilize when knowing where to 
 *  position a control.
 * 
 * @author DISBROB
 */
public class StyleParser {

  private static final Logger LOG = LogManager.getLogger(StyleParser.class);

  private static void applyTextAlignmentCommand(Control control,
      StyleCommand style) {
    // FIXME: does alignment work with other widgets?
    if (control instanceof Label) {
      int alignment = SWT.SEPARATOR;
      if (style.value.equals("left")) {
        alignment = SWT.LEFT;
      } else if (style.value.equals("right")) {
        alignment = SWT.RIGHT;
      } else if (style.value.equals("center")) {
        alignment = SWT.CENTER;
      }
      ((Label) control).setAlignment(alignment);
    }
  }

  private static void applyFloatCommand(Control control, StyleCommand style) {
    BlingLayoutData data = (BlingLayoutData) control.getLayoutData();
    if (data == null) {
      data = new BlingLayoutData();
    }

    if (style.value.equals("left")) {
      data.floatStyle = SWT.LEFT;
    } else if (style.value.equals("right")) {
      data.floatStyle = SWT.RIGHT;
    } else {
      data.floatStyle = SWT.NONE;
    }

    control.setLayoutData(data);
  }

  private static void applyVisibilityCommand(Control control, StyleCommand style) {
    BlingLayoutData data = (BlingLayoutData) control.getLayoutData();
    if (data == null) {
      data = new BlingLayoutData();
    }
    if (style.value.equals("hidden")) {
      data.hidden = true;
    }
    if (style.value.equals("visible")) {
      data.hidden = false;
    }
    control.setLayoutData(data);
  }

  private static void applyHeightStyleCommand(Control control,
      StyleCommand style) {
    BlingLayoutData data = (BlingLayoutData) control.getLayoutData();
    if (data == null) {
      data = new BlingLayoutData();
    }
    data.height = Integer.parseInt(style.value);
    control.setLayoutData(data);
  }

  private static void applyWidthStyleCommand(Control control, StyleCommand style) {
    BlingLayoutData data = (BlingLayoutData) control.getLayoutData();
    if (data == null) {
      data = new BlingLayoutData();
    }
    data.width = Integer.parseInt(style.value);
    control.setLayoutData(data);
  }

  private static void applyFontSizeStyleCommand(Control control,
      StyleCommand style) {
    FontData[] data = control.getFont().getFontData();
    if (data == null) {
      FontData newData = new FontData();
      newData.setHeight(Integer.parseInt(style.value));
      control.setFont(new Font(control.getDisplay(), newData));
    } else {
      for (int i = 0; i < data.length; i++) {
        data[i].setHeight(Integer.parseInt(style.value));
        control.setFont(new Font(control.getDisplay(), data[i]));
      }
    }
  }

  private static Color hexToColor(String hex) {
    if (hex.length() != 7) {
      return new Color(Display.getCurrent(), 0, 0, 0);
    }

    int red = Integer.parseInt(hex.substring(1, 3), 16);
    int blue = Integer.parseInt(hex.substring(3, 5), 16);
    int green = Integer.parseInt(hex.substring(5), 16);

    return new Color(Display.getCurrent(), red, blue, green);
  }

  private static void applyBackgroundColorStyleCommand(Control control,
      StyleCommand style) {

    if (style.value.length() <= 7) {
      control.setBackground(hexToColor(style.value));
    } else {
      LOG.error("Error applying background color style: " + style);
    }
  }

  private static void applyColorStyleCommand(Control control, StyleCommand style) {

    if (style.value.length() <= 7) {
      control.setForeground(hexToColor(style.value));
    } else {
      LOG.error("Error applying color style: " + style);
    }
  }

  private static List parseCommands(String styleCommands)
      throws StyleParserException {
    List retval = new ArrayList();

    StringTokenizer styles = new StringTokenizer(styleCommands, ";");
    while (styles.hasMoreTokens()) {
      String style = styles.nextToken();
      StringTokenizer command = new StringTokenizer(style, " :\t");
      if (command.countTokens() != 2) {
        throw new StyleParserException("invalid style syntax: " + style);
      }
      String name = command.nextToken();
      String value = command.nextToken();
      retval.add(new StyleCommand(name, value));
    }

    return retval;
  }

  public static void applyStyle(Control control, String styleCommands) {
    if (styleCommands == null) {
      return;
    }

    List styles = null;
    try {
      styles = parseCommands(styleCommands);
    } catch (StyleParserException e) {
      LOG.error(e);
      return;
    }

    handleCommands(control, styles);
  }

  private static void handleCommands(Control control, List styles) {
    Iterator commands = styles.iterator();
    while (commands.hasNext()) {
      StyleCommand command = (StyleCommand) commands.next();
      if (command.name.equals("color")) {
        applyColorStyleCommand(control, command);
      } else if (command.name.equals("background-color")) {
        applyBackgroundColorStyleCommand(control, command);
      } else if (command.name.equals("font-size")) {
        applyFontSizeStyleCommand(control, command);
      } else if (command.name.equals("width")) {
        applyWidthStyleCommand(control, command);
      } else if (command.name.equals("height")) {
        applyHeightStyleCommand(control, command);
      } else if (command.name.equals("visibility")) {
        applyVisibilityCommand(control, command);
      } else if (command.name.equals("float")) {
        applyFloatCommand(control, command);
      } else if (command.name.equals("text-align")) {
        applyTextAlignmentCommand(control, command);
      }
    }

  }

}
